
package com.studiofortress.sf.util;

/**
 * This is a Timer with added 'marks'. Marks are points in between the moment of
 * creation and expiration of the Timer. They are evenly distributed across the
 * Timers duration.
 *
 * Often with Timers you also want sub-timers during it. For example a timer for
 * a unit to die, with sub-timers to start explosions during it's death. The
 * MarkedTimer is a mechanism to simulate this where the timer itself is timing
 * it's death and the marks are used for the sub-timer events.
 *
 * Typically the developer will just use isIncrementPastMark to check if a mark
 * has been passed. This will also count down the number of marks.
 *
 * When marks are passed it does not take into account if the timer has expired
 * of not. Essentially if you make a timer that lasts 1 second and has 1 mark
 * and then check for this mark 2 seconds after it has been created the mark
 * will return true even though the timer has also expired.
 *
 * @author Joseph Lenton - JosephLenton@StudioFortress.com
 */
public class MarkedTimer extends Timer
{
    // the amount of time, in milliseconds, between each mark
    private final long markIncrement;

    // the number of marks
    private int numMarks;

    // the time that the next mark will occur at
    private long nextMark;

    /**
     * @param duration
     * @param marks The number of marks to occur during it's lifetime.
     */
    public MarkedTimer(long duration, int marks)
    {
        super( duration );
        if (marks <= 0) {
            throw new IllegalArgumentException("The number of marks cannot be less or equal to 0.");
        }

        numMarks = marks;
        markIncrement = duration / (marks+1);
        nextMark = System.currentTimeMillis() + markIncrement;
    }

    /**
     * Note that this will not increment to the next mark.
     * @return True if the time is currently past the mark, otherwise false.
     */
    public boolean isPastMark()
    {
        return System.currentTimeMillis() > nextMark;
    }

    /**
     * Increments the mark to the next mark point. This is regardless of if the
     * current time has gone past the nextMark point or not.
     * 
     * However if there are no marks left then it will not be incremented. The
     * next mark is actually the end of duration.
     */
    public void incrementMark()
    {
        if (--numMarks > 0) {
            nextMark = nextMark + markIncrement;
        }
    }
    
    /**
     * @return If the time is past the next mark then it is increment and true is returned. Otherwise false is returned.
     */
    public boolean isIncrementPastMark()
    {
        if (numMarks > 0 && isPastMark()) {
            incrementMark();
            return true;
        } else {
            return false;
        }
    }
}
